/**
 * 
 */
package org.starsource.forum.server.dao.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.NoSuchElementException;
import java.util.Set;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import org.starsource.forum.client.exception.DataInconsistantException;
import org.starsource.forum.client.exception.DatabaseException;
import org.starsource.forum.client.exception.MessageNotFoundException;
import org.starsource.forum.server.dao.MessageDao;
import org.starsource.forum.server.dao.ResourceDao;
import org.starsource.forum.server.dao.UserDao;
import org.starsource.forum.server.domain.Message;
import org.starsource.forum.server.domain.Session;

/**
 * @author james
 *
 */
public class MessageDaoJdbc implements MessageDao {

	private JdbcTemplate jdbcTemplate;

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	private UserDao userDao;
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	private ResourceDao resourceDao;
	public void setResourceDao(ResourceDao resourceDao) {
		this.resourceDao = resourceDao;
	}
	
	private static org.apache.log4j.Logger logger = org.apache.log4j.LogManager
	.getLogger(CategoryDaoJdbc.class.getName());

	private class GroupRowCallbackHandler implements RowCallbackHandler {
		private Set<Message> messageSet;
		private Session session;
		
		public void processRow(ResultSet rs) throws SQLException {
			Message message = new Message();
			message.setId(rs.getInt("id"));
			message.setOwnerId(rs.getInt("owner_id"));
			message.setCreateTimestamp(rs.getTimestamp("create_timestamp"));
			message.setUpdateTimestamp(rs.getTimestamp("update_timestamp"));
			message.setContent(rs.getString("content"));
			
			int sessionId = rs.getInt("session_id");
			if (session == null) {
				message.setSessionId(sessionId);
			} else if (sessionId == session.getId()) {
				message.setSession(session);
			} else {
				throw new SQLException("Data inconsistant exception",new DataInconsistantException());
			}

			messageSet.add(message);
		}
		
		public GroupRowCallbackHandler(Set<Message> messageSet) {
			this.messageSet = messageSet;
		}

		public GroupRowCallbackHandler(Set<Message> messageSet, Session session) {
			this.messageSet = messageSet;
			this.session = session;
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.MessageDao#delete(org.starsource.forum.server.domain.Message)
	 */
	@Override
	public void delete(final Message message) throws DatabaseException {
		try {
			resourceDao.delete(message);
			
			final String sql = "DELETE FROM t_message WHERE id = ?";
			jdbcTemplate.update(new PreparedStatementCreator() {
				public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
					PreparedStatement ps = conn.prepareStatement(sql);
					ps.setInt(1, message.getId());
					return ps;
				}
			});
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.MessageDao#getMessage(int)
	 */
	@Override
	public Message getMessage(int id) throws MessageNotFoundException,
			DatabaseException {
		try {
			String sql = "SELECT * FROM t_message WHERE id = ? ";
			final Set<Message> messageSet = new LinkedHashSet<Message>();
			jdbcTemplate.query(sql, new Object[] {id}, new GroupRowCallbackHandler(messageSet));
			Message message = messageSet.iterator().next();
			message.setOwner(userDao.getUser(message.getOwnerId()));
			message.setResources(resourceDao.getResourceSet(message));
			return message;
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		} catch (NoSuchElementException e) {
			logger.error(e.toString());
			throw new MessageNotFoundException();
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.MessageDao#getMessageSet(org.starsource.forum.server.domain.Session)
	 */
	@Override
	public Set<Message> getMessageSet(Session session) throws DatabaseException {
		try {
			String sql = "SELECT * FROM t_message WHERE session_id = ? ";
			final Set<Message> messageSet = new LinkedHashSet<Message>();
			jdbcTemplate.query(sql, new Object[] {session.getId()}, new GroupRowCallbackHandler(messageSet, session));
			Iterator<Message> iterator = messageSet.iterator();
			while (iterator.hasNext()) {
				Message message = iterator.next();
				message.setOwner(userDao.getUser(message.getOwnerId()));
				message.setResources(resourceDao.getResourceSet(message));
			}

			return messageSet;
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.MessageDao#save(org.starsource.forum.server.domain.Message)
	 */
	@Override
	public void save(final Message message) throws DatabaseException {
		final String sql = "INSERT INTO t_message(content, owner_id, session_id, create_timestamp, update_timestamp) VALUES(?,?,?,?,?)";
		KeyHolder keyHolder = new GeneratedKeyHolder();
		jdbcTemplate.update(new PreparedStatementCreator() {
			public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
				PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 
				
				ps.setString(1, message.getContent());
				ps.setInt(2, message.getOwnerId());
				ps.setInt(3, message.getSessionId());
				ps.setTimestamp(4, message.getCreateTimestamp());
				ps.setTimestamp(5, message.getUpdateTimestamp());
				return ps;
			}
		}, keyHolder);
		message.setId(keyHolder.getKey().intValue());
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.MessageDao#update(org.starsource.forum.server.domain.Message)
	 */
	@Override
	public void update(final Message message) throws DatabaseException {
		final String sql = "UPDATE t_message set content = ?, owner_id = ?, session_id = ?, create_timestamp = ?, update_timestamp = ?  WHERE id = ?";
		jdbcTemplate.update(new PreparedStatementCreator() {
			public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
				PreparedStatement ps = conn.prepareStatement(sql);
				ps.setString(1, message.getContent());
				ps.setInt(2, message.getOwnerId());
				ps.setInt(3, message.getSessionId());
				ps.setTimestamp(4, message.getCreateTimestamp());
				ps.setTimestamp(5, message.getUpdateTimestamp());
				ps.setInt(6, message.getId());
				return ps;
			}
		});
	}

	@Override
	public void delete(final Session session) throws DatabaseException {
		try {
			Iterator<Message> iterator = session.getMessages().iterator();
			while (iterator.hasNext()) {
				resourceDao.delete(iterator.next());
			}
			final String sql = "DELETE FROM t_message WHERE session_id = ?";
			jdbcTemplate.update(new PreparedStatementCreator() {
				public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
					PreparedStatement ps = conn.prepareStatement(sql);
					ps.setInt(1, session.getId());
					return ps;
				}
			});
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		}
	}

}
